package com.googlecode.dgwt.client.dojo.core;

import com.google.gwt.core.client.JavaScriptObject;


/**
 * @author	Wellington Tadeu dos Santos
 * @since	DGWT v0.01
 */
public class JsArray<T extends Object> extends JsObject {

    
    protected JsArray(){
        
    }
    
    
    public static <V extends Object> JsArray<V> create() {
        return JavaScriptObject.createArray().<JsArray<V>>cast();
    }

    
    public static native <V extends Object> JsArray<V> create(int capacity) /*-{
        return new Array(capacity);
    }-*/;

    
    /**
     * Casts an object to an JavaScript array
     * 
     * @param obj
     *            object
     * @return the object as JavaScript array
     */
    @SuppressWarnings("unchecked")
    public static <V extends Object> JsArray<V> as(Object[] obj) {
        JsArray<V> array = JsArray.create(obj.length);
        for (int i=0; i<obj.length; i++) {
            array.set(i, (V) obj[i]);
        }
        return array;
    }

    /**
     * Gets the object at a given index.
     * 
     * @param index
     *            the index to be retrieved
     * @return the object at the given index, or <code>null</code> if none
     *         exists
     */
    public final native T get(int index) /*-{
        return this[index];
    }-*/;

    /**
     * Gets the object at a given index.
     * 
     * @param index
     *            the index to be retrieved
     * @return the object at the given index, or <code>null</code> if none
     *         exists
     */
    public final native T get(String index) /*-{
        return this[index];
    }-*/;

        
    /**
     * Gets the length of the array.
     * 
     * @return the array length
     */
    public final native int length() /*-{
        return this.length;
    }-*/;

    
    /**
     * Sets the object value at a given index.
     * 
     * If the index is out of bounds, the value will still be set. The array's
     * length will be updated to encompass the bounds implied by the added
     * object.
     * 
     * @param index
     *            the index to be set
     * @param value
     *            the object to be stored
     */
    public final native void set(int index, T value) /*-{
        this[index] = value;
    }-*/;

    
    /**
     * Sets the object value at a given index.
     * 
     * If the index is out of bounds, the value will still be set. The array's
     * length will be updated to encompass the bounds implied by the added
     * object.
     * 
     * @param index
     *            the index to be set
     * @param value
     *            the object to be stored
     */
    public final native void set(String index, T value) /*-{
        this[index] = value;
    }-*/;

    
    /**
     * Joins two or more arrays and returns the result. This method does not
     * change the existing arrays, it only returns a copy of the joined arrays.
     * 
     * @param arrays
     *            list of arrays to concatenate
     * @return a new JsArray
     */
    public final native JsArray<Object> concat(JsArray<? extends Object> ...arrays) /*-{
        return this.concat.apply(this, @com.googlecode.dgwt.client.dojo.core.JsArray::as([Ljava/lang/Object;)(vetor));
    }-*/;

    
    /**
     * Puts all the elements of an array into a string. The elements are
     * separated by a specified delimiter. The elements will be separated by a
     * specified separator.
     * 
     * @param separator String
     * @return String
     */
    public final native String join(String separator) /*-{
        return this.join(separator);
    }-*/;
    
    
    /**
     * Removes and returns the last element of an array.
     * 
     * @return the element
     */
    public final native T pop() /*-{
        return this.pop();
    }-*/;
    
    
    /**
     * Adds one or more elements to the end of an array and returns the new length.
     * 
     * @return the new length
     */
    public final native int push(T ...elements) /*-{
        return this.push.apply(this, @com.googlecode.dgwt.client.dojo.core.JsArray::as([Ljava/lang/Object;)(elements));
    }-*/;
    
    
    /**
     * Reverses the order of the elements in an array. The reverse() method
     * changes the original array.
     * 
     * @return JsArray
     */
    public final native JsArray<T> reverse() /*-{
        return this.reverse();
    }-*/;
    
    
    /* TODO	 	
        
        shift() 	Removes and returns the first element of an array 	1 	5.5
        slice() 	Returns selected elements from an existing array 	1 	4
        sort() 	Sorts the elements of an array 	1 	4
        splice() 	Removes and adds new elements to an array 	1 	5.5
        unshift() 	Adds one or more elements to the beginning of an array and returns the new length 	1 	6
        valueOf() 	Returns the primitive value of an Array object 	1 	4	 
     */

    
    /**
     * Removes and adds new elements to an array.
     * 
     * @param index
     *            Specify where to add/remove elements.
     * @param howmany
     *            Specify how many elements should be removed.
     * @param elements
     *            Specify a list of new elements to add to the array
     */
    public final native void splice(int index, int howmany, T ...elements) /*-{
        elements = @com.googlecode.dgwt.client.dojo.core.JsArray::as([Ljava/lang/Object;)(elements);
        if (this.splice){
            elements.unshift(index, howmany);
            this.splice.apply(this, elements);
        } else {
            var len = this.length;
            howmany += index;
            if (howmany > len){
                howmany = len;
            }
            if (index < 0){
                index = 0;
            }
            for (var i=index; i<howmany; i++) {
                delete this[i];
                len--;
            }
            for (var j=0; j<elements.length; j++){
                for (var i=len; i>index+j; i--){
                    this[i] = this[i-1];
                }
                this[index+j] = elements[j];
                len++;
            }
        }
    }-*/;


    /**
     * Removes an element from an array.
     * 
     * @param index element index to delete
     */
    public final native void remove(int index) /*-{
        delete this[index];
    }-*/;
    
}
